#include "ByteQueue.h"
#include <assert.h>

ByteQueue::ByteQueue(int cap)
{
	this->capacity = cap;
	queue = new BYTE[capacity];
	size = 0;
	head = tail = 0;
	//hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); /* manual reset event */
	hEvent_not_empty = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto reset event */
	hEvent_not_full = CreateEvent(NULL, FALSE, TRUE, NULL); /* auto reset event */
	InitializeCriticalSection(&mLock);
}


ByteQueue::~ByteQueue(void){
	if(queue != NULL)
		delete[] queue;

	DeleteCriticalSection(&mLock);
	CloseHandle(hEvent_not_empty);
	CloseHandle(hEvent_not_full);
}

int ByteQueue::getSize(){
	return size;
}

BOOL ByteQueue::isFull(){
	//WaitForSingleObject(this->hEvent, INFINITE);
	return (size==capacity)?TRUE:FALSE;
}

BOOL ByteQueue::isEmpty(){
	//WaitForSingleObject(this->hEvent, INFINITE);
	return (size==0)?TRUE:FALSE;
}

void ByteQueue::add(BYTE b){
	
	BOOL done = FALSE;
	while(!done){
		/* if the queue is full then we must wait until it isn't */
		if(WaitForSingleObject(hEvent_not_full, INFINITE)!= WAIT_OBJECT_0)
			assert(FALSE);

		/*
			However, under some (race) conditions, we'll get here and find that
			the queue is already full
		*/
		EnterCriticalSection(&mLock);
		if(!isFull()){
			queue[head] = b;
			head = (head + 1) % capacity;
			size++;
			done = TRUE;
			SetEvent(this->hEvent_not_empty);
			if(!isFull())
				SetEvent(this->hEvent_not_full);
		}
		LeaveCriticalSection(&mLock);
	}
}

void ByteQueue::add(BYTE b[], int len){
	int i, ptr = 0;
	int space, d2End, blockLend, byteRemaining, cpyLen;
	BOOL done = FALSE;
	while(!done){
		/* if the queue is full then we must wait until it isn't */
		if(WaitForSingleObject(hEvent_not_full, INFINITE)!= WAIT_OBJECT_0)
			assert(FALSE);

		/*
			However, under some (race) conditions, we'll get here and find that
			the queue is already full
		*/
		EnterCriticalSection(&mLock);
		if(!isFull()){
			while(ptr < len){
				space = capacity - size;
				d2End = capacity - head;
				blockLend = (space < d2End)?space:d2End;
				byteRemaining = len - ptr;
				cpyLen = (blockLend < byteRemaining)?blockLend : byteRemaining;

				for(i=0; i<cpyLen; i++){
					queue[head + i] = b[ptr + i];
				}
				head = (head + 1) % capacity;
				size += cpyLen;
				ptr += cpyLen;
			}
			done = TRUE;
			SetEvent(this->hEvent_not_empty);
			if(!isFull())
				SetEvent(this->hEvent_not_full);
		}
		LeaveCriticalSection(&mLock);
	}
	
}

BYTE ByteQueue::remove(){
	
	/*If the queue is empty, we must wait until there is something is putted in it*/
	if(WaitForSingleObject(hEvent_not_empty, INFINITE)!= WAIT_OBJECT_0)
		assert(FALSE);
	BYTE b;
	EnterCriticalSection(&mLock);
	if(!isEmpty())
	{
		b = queue[tail];
		tail = (tail + 1) % capacity;
		size--;
		
		SetEvent(this->hEvent_not_full);
		if(!isEmpty())
			SetEvent(this->hEvent_not_empty);
	}
	LeaveCriticalSection(&mLock);
	return b;
	
}

/* Remove all bytes from the queue */
void ByteQueue::remove(BYTE b[], int &len){
	int i, d2End, cpyLen;

	if(WaitForSingleObject(this->hEvent_not_empty, INFINITE)!=WAIT_OBJECT_0){
		len = 0;
		assert(FALSE);
	}

	EnterCriticalSection(&mLock);
	d2End = capacity - tail;
	cpyLen = (size < d2End)? size: d2End;
	for(i=0; i<cpyLen; i++)
		b[i] = b[tail + i];
		
	tail = (tail + size) % capacity;
	size = 0;
	SetEvent(this->hEvent_not_full);
	LeaveCriticalSection(&mLock);
}
